package errorx

import "google.golang.org/grpc/status"

const defaultErrorCode = 3
const defaultCode = 500

type CodeError struct {
	Code      int    `json:"code"`
	ErrorCode int    `json:"errorCode"`
	Msg       string `json:"msg"`
}

type ErrorDetailData struct {
	ErrorCode int    `json:"errorCode,omitempty"`
	Detail    string `json:"detail,omitempty"`
}

type CodeErrorResponse struct {
	Code int             `json:"code"`
	Msg  string          `json:"msg"`
	Data ErrorDetailData `json:"data"`
}

func NewCodeError(errorCode int, msg string) *CodeError {
	return &CodeError{Code: defaultCode, ErrorCode: errorCode, Msg: msg}
}

func NewCodeCanceledError(msg string) error {
	return &CodeError{Code: 500, ErrorCode: defaultErrorCode, Msg: msg}
}

func NewCodeInvalidArgumentError(msg string) error {
	return &CodeError{Code: 500, ErrorCode: defaultErrorCode, Msg: msg}
}

func NewCodeNotFoundError(msg string) error {
	return &CodeError{Code: 404, ErrorCode: defaultErrorCode, Msg: msg}
}

func NewCodeAlreadyExistsError(msg string) error {
	return &CodeError{Code: 500, ErrorCode: defaultErrorCode, Msg: msg}
}

func NewCodeAbortedError(msg string) error {
	return &CodeError{Code: 500, ErrorCode: defaultErrorCode, Msg: msg}
}

func NewCodeInternalError(msg string) error {
	return &CodeError{Code: 500, ErrorCode: defaultErrorCode, Msg: msg}
}

func NewCodeUnavailableError(msg string) error {
	return &CodeError{Code: 500, ErrorCode: defaultErrorCode, Msg: msg}
}

func NewDefaultError(msg string) *CodeError {
	return NewCodeError(defaultErrorCode, msg)
}

func (e *CodeError) WithCode(code int) *CodeError {
	e.Code = code
	return e
}

func (e *CodeError) WithErrorCode(errorCode int) *CodeError {
	e.ErrorCode = errorCode
	return e
}

func (e *CodeError) WithMsg(msg string) *CodeError {
	e.Msg = msg
	return e
}

func (e *CodeError) Error() string {
	return e.Msg
}

func (e *CodeError) Data() *CodeErrorResponse {
	return &CodeErrorResponse{
		Code: e.Code,
		Msg:  e.Msg,
		Data: ErrorDetailData{ErrorCode: e.ErrorCode, Detail: e.Error()},
	}
}

func (e *CodeError) DataWithDetail(detail string) *CodeErrorResponse {
	return &CodeErrorResponse{
		Code: e.Code,
		Msg:  e.Msg,
		Data: ErrorDetailData{ErrorCode: e.ErrorCode, Detail: detail},
	}
}

func NewCodeErrorFromGrpcStatus(err error) *CodeError {
	if newErr, ok := status.FromError(err); ok {
		return NewCodeError(int(newErr.Code()), newErr.Message())
	}
	return NewCodeError(defaultErrorCode, err.Error())
}
